﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlTypes;
using System.Linq;

namespace Cyss.Core
{
    /// <summary>
    /// 
    /// </summary>
    public static class BaseEntityException
    {

        /// <summary>
        /// 获取属性值
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="proName"></param>
        /// <returns></returns>
        public static object GetPropertieValue(this object obj, string proName)
        {
            var pros = obj.GetType().GetCacheNamedMemberAccessors();
            if (pros.ContainsKey(proName))
            {
                return pros[proName].GetValue(obj);
            }
            return null;
        }

        /// <summary>
        /// 设置属性值
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="proName"></param>
        /// <returns></returns>
        public static void SetPropertieValue(this object obj, string proName, object newValue)
        {
            var pros = obj.GetType().GetCacheNamedMemberAccessors();
            if (pros.ContainsKey(proName))
            {
                pros[proName].SetValue(obj, newValue);
            }
        }
        /// <summary>
        /// 将datatable 转换为 list 集合
        /// </summary>
        /// <typeparam name="T">集合实体类型</typeparam>
        /// <param name="dt">数据表</param>
        /// <returns></returns>
        public static IList<T> ToList<T>(this DataTable dt) where T : class, new()
        {
            IList<T> list = new List<T>();
            //table 列的集合
            DataColumnCollection columns = dt.Columns;

            T tt = new T();
            var pros = tt.GetType().GetCacheNamedMemberAccessors();

            //循环数据插入集合中
            foreach (DataRow row in dt.Rows)
            {
                T t = new T();

                foreach (DataColumn column in columns)
                {
                    if (!pros.ContainsKey(column.ColumnName))
                    {
                        continue;
                    }
                    var pro = pros[column.ColumnName];

                    if (!pro.CanWrite) continue;

                    if (row[column.ColumnName] != System.DBNull.Value)
                    {
                        var strValue = row[column.ColumnName];
                        pro.SetValue(t, strValue);
                    }
                }
                list.Add(t);
            }
            return list;
        }

        /// <summary>
        /// 将集合转换成DataTable
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        /// <returns></returns>
        public static DataTable ToDataTable<T>(this IEnumerable<T> list) where T : class, new()
        {
            var type = list.GetListItemType();
            var tb = new DataTable(type.Name);
            var props = type.GetCacheNamedMemberAccessors();

            foreach (var prop in props.Values)
            {
                tb.Columns.Add(prop.ProjectName);
            }

            foreach (var item in list)
            {
                DataRow row = tb.NewRow();
                foreach (DataColumn column in tb.Columns)
                {
                    if (!props.ContainsKey(column.ColumnName))
                    {
                        continue;
                    }
                    var pro = props[column.ColumnName];
                    row[column.ColumnName] = pro.GetValue(item);
                }
                tb.Rows.Add(row);
            }

            return tb;
        }


        /// <summary>
        /// 给model 值为null的属性设置默认值并将字符串去掉首位空格
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        public static void SetDefaultValue<T>(this T t) where T : class, new()
        {
            if (t == null)
            {
                return;
            }
            var pros = t.GetType().GetCacheNamedMemberAccessors();
            foreach (var pro in pros.Values)
            {
                if (!pro.CanWrite)
                {
                    continue;
                }
                object value = pro.GetValue(t);
                Type type = pro.PropertyType;
                if (value != null && type == typeof(System.String))
                {
                    pro.SetValue(t, value.ToString().Trim());
                }
                if (value == null && type == typeof(System.String))
                {
                    pro.SetValue(t, string.Empty);
                }
                if (type == typeof(System.DateTime) && Convert.ToDateTime(value) == DateTime.MinValue)
                {
                    pro.SetValue(t, SqlDateTime.MinValue.Value);
                }
            }
        }

        /// <summary>
        /// 复制一个实体
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <returns></returns>
        public static T Copy<T>(this T t)
        {
            return JsonHelper.DeserializeObject<T>(JsonHelper.SerializeObject(t));
        }

        /// <summary>
        /// 获取实体修改的字段集合
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="AfterT">修改前实体</param>
        /// <param name="BeforeT">修改后实体</param>
        /// <returns></returns>
        public static IEnumerable<ChangeValueModel> GetChangeValues<T>(this T AfterT, T BeforeT)
        {
            IList<ChangeValueModel> changeValueModels = new List<ChangeValueModel>();
            var pros = AfterT.GetType().GetCacheNamedMemberAccessors();
            foreach (var pro in pros.Values)
            {
                var newValue = pro.GetValue(BeforeT);
                var oldValue = pro.GetValue(AfterT);
                if (!IsEqualsValue(pro.PropertyType, newValue, oldValue))
                {
                    changeValueModels.Add(new ChangeValueModel { Field = pro.ProjectName, NewValue = newValue.ToNullString(), OldValue = oldValue.ToNullString() });
                }
            }
            return changeValueModels;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="SourceType"></param>
        /// <param name="newValue"></param>
        /// <param name="oldValue"></param>
        /// <returns></returns>
        private static bool IsEqualsValue(Type SourceType, object newValue, object oldValue)
        {
            if ((newValue == null && oldValue != null) || (newValue != null && oldValue == null))
            {
                return false;
            }

            if (SourceType == typeof(decimal) || SourceType == typeof(decimal?))
            {
                return Convert.ToDecimal(newValue) == Convert.ToDecimal(oldValue);

            }

            if (SourceType == typeof(double) || SourceType == typeof(double?))
            {
                return Convert.ToDecimal(newValue) == Convert.ToDecimal(oldValue);
            }

            if (SourceType == typeof(DateTime) || SourceType == typeof(DateTime?))
            {
                return Convert.ToDateTime(newValue) == Convert.ToDateTime(oldValue);
            }

            if (SourceType == typeof(short) || SourceType == typeof(short?)
                || SourceType == typeof(int) || SourceType == typeof(int?)
                || SourceType == typeof(long) || SourceType == typeof(long?)
                )
            {
                return Convert.ToInt64(newValue) == Convert.ToInt64(oldValue);
            }
            return newValue.ToNullString().Trim() == oldValue.ToNullString().Trim();
        }



        public static List<PropertyAccessorHandler> GetCachePropertyAccessorHandlers(this BaseEntity model)
        {

            var type = PropertyAccessorHandlers._propertyAccessorHandlers.FirstOrDefault(x => x.MemberType == model.GetType());
            if (type != null)
            {
                return type.propertyAccessorHandlers;
            }

            PropertyAccessorHandlers propertyAccessorHandlers = new PropertyAccessorHandlers();

            propertyAccessorHandlers.MemberType = model.GetType();
            System.Reflection.PropertyInfo[] proModels = model.GetType().GetProperties();

            foreach (var pro in proModels)
            {
                propertyAccessorHandlers.propertyAccessorHandlers.Add(new PropertyAccessorHandler(pro));

            }
            PropertyAccessorHandlers._propertyAccessorHandlers.Add(propertyAccessorHandlers);
            return propertyAccessorHandlers.propertyAccessorHandlers;


        }

        private static object ObjCacheNamedMemberAccessor = new object();
        public static Dictionary<string, INamedMemberAccessor> GetCacheNamedMemberAccessors(this Type type)
        {
            string fullName = type.FullName;
            if (NamedMemberAccessors._propertyAccessorHandlers.ContainsKey(fullName))
            {
                return NamedMemberAccessors._propertyAccessorHandlers[fullName].propertyAccessorHandlers;
            }


            lock (ObjCacheNamedMemberAccessor)
            {
                if (NamedMemberAccessors._propertyAccessorHandlers.ContainsKey(fullName))
                {
                    return NamedMemberAccessors._propertyAccessorHandlers[fullName].propertyAccessorHandlers;
                }

                NamedMemberAccessors propertyAccessorHandlers = new NamedMemberAccessors();

                propertyAccessorHandlers.MemberType = type;
                System.Reflection.PropertyInfo[] proModels = type.GetProperties();

                foreach (var propertyInfo in proModels)
                {

                    var accessor = Activator.CreateInstance(typeof(PropertyAccessor<,>).MakeGenericType(propertyAccessorHandlers.MemberType, propertyInfo.PropertyType), propertyAccessorHandlers.MemberType, propertyInfo.Name) as INamedMemberAccessor;

                    propertyAccessorHandlers.propertyAccessorHandlers.Add(propertyInfo.Name, accessor);

                }
                NamedMemberAccessors._propertyAccessorHandlers.Add(type.FullName, propertyAccessorHandlers);
                return propertyAccessorHandlers.propertyAccessorHandlers;

            }


        }

        //public static List<INamedMemberAccessor> GetCacheNamedMemberAccessors(this object model)
        //{

        //    return GetCacheNamedMemberAccessors(model.GetType().GetCacheNamedMemberAccessors());


        //}



        private class NamedMemberAccessors
        {

            public static Dictionary<string, NamedMemberAccessors> _propertyAccessorHandlers = new Dictionary<string, NamedMemberAccessors>();
            public NamedMemberAccessors()
            {
                this.propertyAccessorHandlers = new Dictionary<string, INamedMemberAccessor>();
            }
            public Type MemberType { set; get; }

            public Dictionary<string, INamedMemberAccessor> propertyAccessorHandlers { set; get; }
        }

        private class PropertyAccessorHandlers
        {

            public static List<PropertyAccessorHandlers> _propertyAccessorHandlers = new List<PropertyAccessorHandlers>();
            public PropertyAccessorHandlers()
            {
                this.propertyAccessorHandlers = new List<PropertyAccessorHandler>();
            }
            public Type MemberType { set; get; }

            public List<PropertyAccessorHandler> propertyAccessorHandlers { set; get; }
        }

    }
}
